﻿#include "DownloadTsFileManager.h"
#include "MyHttp.h"
#include <QFile>
#include <QDateTime>
#include <QRandomGenerator>
#include <QDir>
#include <QProcess>
#include <QJsonValue>
#include <QJsonDocument>
#include <QCryptographicHash>
#include <qt_windows.h>
//去掉文件路径中的非法字符
//strUrl.remove(QRegularExpression("[\\\\/:\\*\\?\"<>\\|\\.]")


#define ARIA2_TOKEN QString("token:123456")


DownloadTsFileManager::DownloadTsFileManager(QString strUrl, QString strSavePath)
    : QObject(nullptr)
    , m_strUrl(strUrl)
    , m_strSavePath(strSavePath)
{
    QFileInfo fileSavePath(strSavePath);
    QDir().mkpath(fileSavePath.absolutePath());
    m_strTmpDir = fileSavePath.absolutePath() + "/" + QCryptographicHash::hash(strUrl.toUtf8(), QCryptographicHash::Md5).toHex();
    QDir().mkpath(m_strTmpDir);
    m_strTsDir = m_strTmpDir + "/" + "ts";
    QDir().mkpath(m_strTsDir);

    m_strAria2Url = "http://localhost:6800/jsonrpc";
}

DownloadTsFileManager::~DownloadTsFileManager()
{

}

void DownloadTsFileManager::DownTsFiles()
{
    DownM3u8File();
    VerifyM3u8File();
    QStringList  lstUrl = AnalysisM3u8();
    PostTsToAria2(lstUrl);

    while (getGlobalStatas() <= 0)
    {
        QThread::sleep(10);
    }

    MergeTsFiles();

    DeleteTempFiles();
}

void DownloadTsFileManager::DeleteTempFiles()
{
    bool isDelTemp = this->property("isDeleteTempFiles").toBool();
    if (!isDelTemp)
    {
        return;
    }

    QString strTempPath = m_strTmpDir;
    if(strTempPath.length() < 35)
    {
        return;
    }

    QDir dir(strTempPath);
    if(!dir.exists())
    {
        return;
    }

    bool bRet = dir.removeRecursively();
    emit sigDownMessage(TR("删除临时文件完成 : ") + QString::number(bRet));
}

void DownloadTsFileManager::DownM3u8File()
{
    m_strM3u8File = QString("%1/%2.m3u8").arg(m_strTmpDir).arg(QDateTime::currentMSecsSinceEpoch());

    for (int i=0; i<100000; i++)
    {
        emit sigDownMessage(TR("开始下载 : ") + m_strUrl);

        QString strUrl = m_strUrl;
        QString strRet = MyHttp::Get(strUrl);

        if (!strRet.isEmpty())
        {
            m_strM3u8File = QString("%1/%2.m3u8").arg(m_strTmpDir).arg(QDateTime::currentMSecsSinceEpoch());
            QFile file(m_strM3u8File);
            file.open(QFile::ReadWrite);
            file.write(strRet.toUtf8());

            emit sigDownMessage(TR("下载完成 : ") + m_strUrl);
            return;
        }
        else
        {
            emit sigDownMessage(TR("下载失败 : ") + m_strUrl);
        }
    }
}

QString DownloadTsFileManager::DownKeyFile(QString keyUri)
{
    QString keyfile = QString("%1/key.key").arg(m_strTmpDir);
    QStringList keyList = keyUri.split("\"");
    if (keyList.size() > 1)
    {
        QString keyurl = keyUri.split("\"").at(1);
        if (keyurl.startsWith("http", Qt::CaseInsensitive))
        {
        }
        else
        {
            keyurl = m_strUrl.left(m_strUrl.lastIndexOf("/")) + "/" + keyurl;
        }
        emit sigDownMessage(TR("开始下载 : ") + keyurl);
        QString strRet = MyHttp::Get(keyurl);
        if (!strRet.isEmpty())
        {
            QFile file(keyfile);
            file.open(QFile::ReadWrite);
            file.write(strRet.toUtf8());

            emit sigDownMessage(TR("下载完成 : ") + keyurl);
            return QString(strRet.toLatin1().toHex());
        }
        else
        {
            emit sigDownMessage(TR("下载失败 : ") + keyurl);
        }
    }
    return "NO";
}

int DownloadTsFileManager::VerifyM3u8File()
{
    emit sigDownMessage(TR("校验开始 : ") + m_strM3u8File);

    bool iNextIsTsUrl = false;

    QFile file(m_strM3u8File);
    file.open(QFile::ReadOnly);
    QString strLine = file.readLine();
    while (!strLine.isEmpty() || file.canReadLine())
    {
        if (strLine.startsWith("#EXT-X-KEY"))
        {
            m_key = DownKeyFile(strLine);
        }
        else if (strLine.startsWith("#EXTINF:"))
        {
            iNextIsTsUrl = true;
        }
        else if (!strLine.startsWith("#EXT"))
        {
            strLine = strLine.trimmed();
            if (strLine.endsWith(".m3u8"))
            {
                if (strLine.startsWith("/"))
                {
                    m_strUrl = m_strUrl.left(m_strUrl.indexOf("/", 8)) + "/" +strLine;
                }
                else
                {
                    m_strUrl = m_strUrl.left(m_strUrl.lastIndexOf("/")) + "/" +strLine;
                }
                DownM3u8File();
                VerifyM3u8File();
                emit sigDownMessage(TR("校验完成 : ") + m_strM3u8File);
                return 1;
            }
            else if (strLine.endsWith(".ts"))
            {
                emit sigDownMessage(TR("校验完成 : ") + m_strM3u8File);
                return 1;
            }
            else if (iNextIsTsUrl && (strLine.startsWith("http") || strLine.startsWith("/")) )
            {
                emit sigDownMessage(TR("校验完成 : ") + m_strM3u8File);
                return 1;
            }
        }
        strLine = file.readLine();
    }

    emit sigDownMessage(TR("校验失败 : ") + m_strM3u8File);
    return 0;
}

QStringList DownloadTsFileManager::AnalysisM3u8()
{
    QStringList lstUrl;

    emit sigDownMessage(TR("解析Ts路径 : ") + m_strM3u8File);

    QFile file(m_strM3u8File);
    file.open(QFile::ReadWrite);
    QString strLine = file.readLine();
    int iNum = 10000;
    while (!strLine.isEmpty() || file.canReadLine())
    {
       if (!strLine.startsWith("#EXT"))
        {
            strLine = strLine.trimmed();
            QString strUrl;
            if (strLine.startsWith("http", Qt::CaseInsensitive))
            {
                strUrl = strLine;
                lstUrl.append(strUrl);
                m_lstTsName.append(strLine);
                ++iNum;
                m_lstFileName.append("a"+QString::number(iNum)+".ts");
            }
            else if (strLine.endsWith(".ts"))
            {
                if (strLine.startsWith("/"))
                {
                    strUrl = m_strUrl.left(m_strUrl.indexOf("/", 8)) + "/" +strLine;
                }
                else
                {
                    strUrl = m_strUrl.left(m_strUrl.lastIndexOf("/")) + "/" +strLine;
                }
                lstUrl.append(strUrl);
                m_lstTsName.append(strLine);
                ++iNum;
                m_lstFileName.append("a"+QString::number(iNum)+".ts");
            }
        }
        strLine = file.readLine();
    }

    emit sigDownMessage(TR("解析Ts路径完成 : ") + m_strM3u8File);
    return lstUrl;
}

void DownloadTsFileManager::PostTsToAria2(const QStringList& lstUrl)
{
    static int s_id = 1;

    emit sigDownMessage(TR("下载Ts文件请求 : aria2.addUri"));

    int iNum = -1;
    foreach (QString var, lstUrl)
    {
        iNum++;
        QFileInfo fileInfo(m_strTsDir +"/" +m_lstFileName[iNum]);
        if (fileInfo.exists() && (fileInfo.size() > 10 ))
        {
            continue;
        }

        QJsonArray jsonUrl;
        jsonUrl.append(QJsonValue(var));

        QJsonObject jsonConf;
        jsonConf.insert("dir", m_strTsDir);
        jsonConf.insert("out", m_lstFileName[iNum]);
        jsonConf.insert("split", "10");
        jsonConf.insert("max-connection-per-server", "10");

        QJsonArray jsonArr;
        jsonArr.append(QJsonValue(ARIA2_TOKEN));
        jsonArr.append(jsonUrl);
        jsonArr.append(jsonConf);


        QJsonObject jsonObj;
        jsonObj.insert("jsonrpc", "2.0");
        jsonObj.insert("method", "aria2.addUri");
        jsonObj.insert("id", s_id++);
        jsonObj.insert("params", jsonArr);

        QJsonArray json;
        json.append(jsonObj);

        QJsonDocument document(json);
        QString strData = document.toJson(QJsonDocument::Compact);

        QString strRet = MyHttp::Post(m_strAria2Url, strData);

        QJsonDocument documentRet = QJsonDocument::fromJson(strRet.toUtf8());
        if(documentRet.isArray())
        {
            QJsonArray jsonArrRet = documentRet.array();
            if (jsonArrRet.count() > 0)
            {
                QJsonObject jsonObjRet = jsonArrRet.at(0).toObject();
                m_lstTsGid.append(jsonObjRet.value("result").toString());
            }
        }
    }

    emit sigDownMessage(TR("下载Ts文件请求完成 : aria2.addUri"));
}

int DownloadTsFileManager::getGlobalStatas()
{
    QJsonArray jsonArr;
    jsonArr.append(QJsonValue(ARIA2_TOKEN));

    QJsonObject jsonObj;
    jsonObj.insert("jsonrpc", "2.0");
    jsonObj.insert("method", "aria2.getGlobalStat");
    jsonObj.insert("id", 1);
    jsonObj.insert("params", jsonArr);

    QJsonDocument document(jsonObj);
    QString strData = document.toJson(QJsonDocument::Compact);

    QString strRet = MyHttp::Post(m_strAria2Url, strData);

    QJsonDocument documentRet = QJsonDocument::fromJson(strRet.toUtf8());
    if(documentRet.isObject())
    {
        QJsonObject jsonObjRet = documentRet.object();
        jsonObjRet = jsonObjRet.value("result").toObject();
        int numActive = jsonObjRet.value("numActive").toString().toInt();
        int numWaiting = jsonObjRet.value("numWaiting").toString().toInt();
        int numStoppedTotal = jsonObjRet.value("numStoppedTotal").toString().toInt();

        emit sigDownMessage(TR("下载Ts状态 : ") + QString(TR("正在下载：%1; 等待中：%2; 已完成：%3 ")).arg(numActive).arg(numWaiting).arg(numStoppedTotal));

        if (numActive == 0 && numWaiting == 0)
        {
            if (numStoppedTotal >= m_lstTsGid.count())
            {
                return 1;
            }
        }
        return -1;
    }
    else
    {
        emit sigDownMessage(TR("下载Ts状态 : 请求失败！ 0  0  0"));
    }

    return 0;
}

int DownloadTsFileManager::purgeDownloadResult()
{
    QJsonArray jsonArr;
    jsonArr.append(QJsonValue(ARIA2_TOKEN));

    QJsonObject jsonObj;
    jsonObj.insert("jsonrpc", "2.0");
    jsonObj.insert("method", "aria2.purgeDownloadResult");
    jsonObj.insert("id", 1);
    jsonObj.insert("params", jsonArr);

    QJsonDocument document(jsonObj);
    QString strData = document.toJson(QJsonDocument::Compact);

    QString strRet = MyHttp::Post(m_strAria2Url, strData);

    QJsonDocument documentRet = QJsonDocument::fromJson(strRet.toUtf8());
    if(documentRet.isObject())
    {
        QJsonObject jsonObjRet = documentRet.object();
        QString sRet = jsonObjRet.value("result").toString();

        emit sigDownMessage(TR("清理下载列表 : ") + sRet);
    }
    else
    {
        emit sigDownMessage(TR("清理下载列表 : 失败！"));
    }

    return 0;
}

void DownloadTsFileManager::MergeTsFiles()
{
    emit sigDownMessage(TR("合并ts文件 : ") + m_strTsDir);

    QFile file(m_strSavePath);
    file.open(QFile::WriteOnly);

    foreach (QString var, m_lstFileName)
    {
        if (m_key.compare("NO") != 0)
        {
            QString tmpfile = var;
            var = tmpfile+"_jm";
            QFileInfo fileInfo(m_strTsDir +"/" +var);
            if (fileInfo.exists() && (fileInfo.size() > 10))
            {
            }
            else
            {
                QProcess process;
                QString program = "C:/OpenSSL/OpenSSL-Win64/bin/openssl.exe";
                QStringList arguments;
                arguments.append("aes-128-cbc");
                arguments.append("-d");
                arguments.append("-in");
                arguments.append(m_strTsDir +"/" + tmpfile);
                arguments.append("-out");
                arguments.append(m_strTsDir +"/" + var);
                arguments.append("-iv");
                arguments.append("00000000000000000000000000000000");
                arguments.append("-K");
                arguments.append(m_key);
                process.start(program, arguments);
                process.waitForFinished();
            }
        }

        QFile fileVar(m_strTsDir +"/" + var);
        if ( (fileVar.size() > 0)
            && (fileVar.open(QFile::ReadOnly)) )
        {
            QByteArray baContent = fileVar.readAll();
            QByteArray baPre; baPre.append(char(0x89));baPre.append(char(0x50));baPre.append(char(0x4e));
            if(baContent.startsWith(baPre))
            {
                QByteArray baPreMp4; baPreMp4.append(char(0x47));baPreMp4.append(char(0x40));baPreMp4.append(char(0x11));
                int index = baContent.indexOf(baPreMp4);
                file.write(baContent.mid(index));
            }
            else
            {
                file.write(baContent);
            }
        }
        else
        {
            emit sigDownMessage(TR("合并ts文件 : 缺少文件：") + var);
        }

    }

    emit sigDownMessage(TR("下载完成 : ") + m_strSavePath);
}
